在学习Python的时候,我们经常看到__getattribute__
和__getattr__
这两个长得很像的,那他们之间有什么联系和区别呢?
实例属性的获取和拦截
当访问某个实例属性时, __getattribute__
会被无条件调用,如未实现自己的__getattr__
方法,会抛出AttributeError提示找不到这个属性,如果自定义了自己__getattr__
方法的话,方法会在这种找不到属性的情况下被调用。所以在找不到属性的情况下通过实现自定义的__getattr__
方法来实现一些功能是一个不错的方式,因为它不会像__getattribute__
方法每次都会调用可能会影响一些正常情况下的属性访问:1
2
3
4
5
6class Test(object):
def __init__(self, param):
self.param = param
def __getattr__(self, item):
return 'default'
注意,这里的属性指的是实例属性(instance object) 而非类属性!!,其实对类来说也有__getattribute__
方法,但是只有实例属性才有__getattribute__
和__getattr__
的区别
函数的详细信息
函数的签名:1
2__getattr__: __getattr__(self, name)
__getattribute__:__getattribute__(self, name)
两个方法接受的参数都一样,那么他们的区别呢:
当访问一个不存在的实例属性的时候就会抛出 AttributeError异常,这个异常是由
__getattribute__(self, name)
抛出的,这是因为__getattribute__()
会被无条件调用。
当属性不在实例的__dict__
中并且不在基类及祖先类的__dict__
中而触发AttributeError时,__getattr__()
才会被调用
需要注意的问题
避免无穷递归的问题
下面代码中会产生无穷递归的问题1
2
3
4
5
6class A:
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
这是因为属性访问调用的是覆盖了默认的__getattribute__()
的方法,而在该方法中的self.__dict__[attr]
又会递归的调用这个方法,于是产生了无穷递归。正确的做法是调用super(obj, self).__getattribute__(self, attr)
,所以上面的代码可以改为super(A, self).__getattribute__(self,attr)
。
访问未定义的属性
如果在__getattr__()
方法中不抛出AttributeError或者显式返回一个值,则会返回None
(其实在普通函数中也一样返回None
),此时可能会影响到程序的实际运行,我们来看一个例子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class A(object):
def __init__(self, name):
self.name = name
self.x = 20
def __getattr__(self, name):
if name == 'y':
return self.x **2
elif name == 'x':
return self.x**3
def __getattribute__(self, name):
try:
return super(A,self).__getattribute__(attr)
except KeyError:
return 'default'
a = A('attribute')
print a.name
print a.z
if hasattr(a, 't'):
c = a.t
print (c)
else:
print ("instance has no attribute t")
在调用hasattr()
时,__getattr__()
返回的是None而不是抛出一个异常,所以hasattr()
的返回值是True
,因此这段程序将输出None
而不是警告信息。